#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <cassert>

using namespace std;

// 进程池

typedef void (*functor)();

vector<functor> functors; // 方法集合

//for debug
unordered_map<uint32_t, string> info;

void f1()
{
    cout << "这是一个处理日志的任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n"
         << endl;
}

void f2()
{
    cout << "这是一个备份数据任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n"
         << endl;
}
void f3()
{
    cout << "这是一个处理网络连接的任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n"
         << endl;
}

void loadFunctor()
{
    info.insert({functors.size(), "处理日志的任务"});
    functors.push_back(f1);

    info.insert({functors.size(), "备份数据任务"});
    functors.push_back(f2);

    info.insert({functors.size(), "网络连接的任务"});
    functors.push_back(f3);
}

// 第一个int32_t: 进程pid，第二个int32_t: 该进程对应的管道写端fd
typedef pair<int32_t, int32_t> elem;
int processNum = 5;

void work(int blockFd)
{
    // 子进程核心工作的代码
    while (true)
    {
        // a. 阻塞等待    b. 获取任务信息
        uint32_t operatorCode = 0;
        ssize_t s = read(blockFd, &operatorCode, sizeof(uint32_t));

        if (s == 0) break;
        cout << "进程[" << getpid() << "]" << "开始工作" << endl;

        assert(s == sizeof(uint32_t));
        (void)s;// 编程小技巧

        if (operatorCode < functors.size())
        {
            // c. 处理任务
            functors[operatorCode]();
        }
        else
        {
            cerr << "bug? operatorCode = " << operatorCode << endl;
        }
    }
    cout << "进程[" << getpid() << "]" << "结束工作" << endl;
}

// [子进程的pid, 子进程的管道fd]
void blanceSendTask(const vector<elem>& processFds)
{
   srand((long long)time(nullptr));
   
    // 随机给某个进程派发随机某个任务：
    // uint32_t cnt = 10;
    // while (cnt--)
    // {
    //     sleep(1);
    //     // 选择一个进程，选择进程是随机的，没有压着一个进程给任务
    //     // 较为均匀的将任务给所有的子进程 -- 负载均衡
    //     uint32_t pick = rand() % processFds.size();

    //     // 选择一个任务
    //     uint32_t task = rand() % functors.size();

    //     // 把任务给一个指定的进程
    //     write(processFds[pick].second, &task, sizeof(task));

    //     // 打印对应的提示信息
    //     cout << "父进程指派任务->" << info[task] << "给进程: " 
    //         << processFds[pick].first << "编号: " << pick << endl;
    // }

    // 将这几个进程创建的管道的写端给挨个关上
    // for(int i = 0; i < processFds.size(); i++)
    // {
    //     close(processFds[i].second);
    // }

    // 给这几个进程挨个派发随机任务；
    for (int i = 0; i < processFds.size(); i++)
    {
       sleep(1);
       int j = rand() % functors.size();
       write(processFds[i].second, &j, sizeof(int));
       close(processFds[i].second);
    }
}

int main()
{
    // 加载任务列表
    loadFunctor();
    vector<elem> assignMap;

    // 创建processNum个进程
    for (int i = 0; i < processNum; i++)
    {
        // 定义管道保存fd的对象
        int pipefd[2] = { 0 };
        if (pipe(pipefd) != 0)
        {
            cerr << "pipe error" << endl;
            return 1;
        }

        // 创建子进程
        pid_t id = fork();
        if (id == 0)
        {
            // 子进程执行，read -> pipefd[0]
            close(pipefd[1]);

            // 子进程执行
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }
        else if(id > 0)
        {
            // 父进程做的事情，pipefd[1]
            close(pipefd[0]);
            elem e(id, pipefd[1]);
            // 将创建出来的子进程的pid存起来
            assignMap.push_back(e);
        }
    }
    cout << "creat all process success!\n" << endl;

    // 父进程，派发任务
    blanceSendTask(assignMap);

    // 回收资源
    for (int i = 0; i < processNum; i++)
    {
        if (waitpid(assignMap[i].first, nullptr, 0) > 0) 
            cout << "wait for:" << assignMap[i].first << " wait success! " 
            << " number: " << i << endl;
    }
    cout << "----------------------------程序结束----------------------------" << endl;

    return 0;
}
